using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Beaker.OpenCube.Game;
using OpenCube.Nbt;
using OpenCube.Nbt.Mapping;

namespace OpenCube.Model.Primitives.Converters
{
	public class CoordinatesConverter
		: NbtMultiTypeConverter
	{
		protected override bool CanConvertToNbt(Type sourceModel)
		{
			return sourceModel == typeof(Coordinates2<int>)
				  || sourceModel == typeof(Coordinates3<int>)
				  || sourceModel == typeof(Coordinates2<float>)
				  || sourceModel == typeof(Coordinates3<float>)
				  || sourceModel == typeof(Coordinates2<double>)
				  || sourceModel == typeof(Coordinates3<double>);
		}

		protected override bool CanConvertToModel(IReadOnlyList<Type> sourceNbtTypes, Type targetModelType)
		{
			// not 2 or 3 items? done.
			if (sourceNbtTypes.Count < 2 || sourceNbtTypes.Count > 3)
			{
				// 1, then allow when a list of doubles
				if (sourceNbtTypes.Count == 1 && typeof(IList<double>).GetTypeInfo().IsAssignableFrom(sourceNbtTypes[0].GetTypeInfo()))
					return true;

				return false;
			}

			// not all types the same? done.
			if (sourceNbtTypes.Skip(1).Any(t => t != sourceNbtTypes[0]))
				return false;

			if (!targetModelType.GetTypeInfo().IsGenericType)
				return false;
			if (targetModelType.GetTypeInfo().GetGenericTypeDefinition() != (sourceNbtTypes.Count == 2 ? typeof(Coordinates2<>) : typeof(Coordinates3<>)))
				return false;

			// type not matching with argument in type
			if (targetModelType.GenericTypeArguments[0] != sourceNbtTypes.First())
				return false;

			return true;
		}

		protected override object ConvertToModelValue(IConversionContext conversionContext, IReadOnlyList<object> nbtValue)
		{
			if (nbtValue.Count == 3)
			{
				if (nbtValue[0] is int)
					return new Coordinates3<int>((int)nbtValue[0], (int)nbtValue[1], (int)nbtValue[2]);
				if (nbtValue[0] is float)
					return new Coordinates3<float>((float)nbtValue[0], (float)nbtValue[1], (float)nbtValue[2]);
				if (nbtValue[0] is double)
					return new Coordinates3<double>((double)nbtValue[0], (double)nbtValue[1], (double)nbtValue[2]);
			}

			if (nbtValue.Count == 2)
			{
				if (nbtValue[0] is int)
					return new Coordinates2<int>((int)nbtValue[0], (int)nbtValue[1]);
				if (nbtValue[0] is float)
					return new Coordinates2<float>((float)nbtValue[0], (float)nbtValue[1]);
				if (nbtValue[0] is double)
					return new Coordinates2<double>((double)nbtValue[0], (double)nbtValue[1]);
			}

			if (nbtValue.Count == 1)
			{
				// list of doubles?
				IList<double> list = (IList<double>)nbtValue[0];
				if (list.Count == 2)
					return new Coordinates2<double>(list[0], list[1]);
				if (list.Count == 3)
					return new Coordinates3<double>(list[0], list[1], list[2]);
			}

			throw new NotImplementedException();
		}

		protected override IEnumerable<object> ConvertToNbtValue(IConversionContext conversionContext, object modelValue)
		{
			throw new NotImplementedException();
		}
	}
}